home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / src / hotlist.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  33.5 KB  |  1,113 lines

  1. /****************************************************************************
  2.  * NCSA Mosaic for the X Window System                                      *
  3.  * Software Development Group                                               *
  4.  * National Center for Supercomputing Applications                          *
  5.  * University of Illinois at Urbana-Champaign                               *
  6.  * 605 E. Springfield, Champaign IL 61820                                   *
  7.  * mosaic@ncsa.uiuc.edu                                                     *
  8.  *                                                                          *
  9.  * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
  10.  *                                                                          *
  11.  * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
  12.  * copyrighted by The Board of Trustees of the University of Illinois       *
  13.  * (UI), and ownership remains with the UI.                                 *
  14.  *                                                                          *
  15.  * The UI grants you (hereafter, Licensee) a license to use the Software    *
  16.  * for academic, research and internal business purposes only, without a    *
  17.  * fee.  Licensee may distribute the binary and source code (if released)   *
  18.  * to third parties provided that the copyright notice and this statement   *
  19.  * appears on all copies and that no charge is associated with such         *
  20.  * copies.                                                                  *
  21.  *                                                                          *
  22.  * Licensee may make derivative works.  However, if Licensee distributes    *
  23.  * any derivative work based on or derived from the Software, then          *
  24.  * Licensee will (1) notify NCSA regarding its distribution of the          *
  25.  * derivative work, and (2) clearly notify users that such derivative       *
  26.  * work is a modified version and not the original NCSA Mosaic              *
  27.  * distributed by the UI.                                                   *
  28.  *                                                                          *
  29.  * Any Licensee wishing to make commercial use of the Software should       *
  30.  * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
  31.  * commercial use.  Commercial use includes (1) integration of all or       *
  32.  * part of the source code into a product for sale or license by or on      *
  33.  * behalf of Licensee to third parties, or (2) distribution of the binary   *
  34.  * code or source code to third parties that need it to utilize a           *
  35.  * commercial product sold or licensed by or on behalf of Licensee.         *
  36.  *                                                                          *
  37.  * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
  38.  * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
  39.  * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
  40.  * USERS OF THIS SOFTWARE.                                                  *
  41.  *                                                                          *
  42.  * By using or copying this Software, Licensee agrees to abide by the       *
  43.  * copyright law and all other applicable laws of the U.S. including, but   *
  44.  * not limited to, export control laws, and the terms of this license.      *
  45.  * UI shall have the right to terminate this license immediately by         *
  46.  * written notice upon Licensee's breach of, or non-compliance with, any    *
  47.  * of its terms.  Licensee may be held legally responsible for any          *
  48.  * copyright infringement that is caused or encouraged by Licensee's        *
  49.  * failure to abide by the terms of this license.                           *
  50.  *                                                                          *
  51.  * Comments and questions are welcome and can be sent to                    *
  52.  * mosaic-x@ncsa.uiuc.edu.                                                  *
  53.  ****************************************************************************/
  54.  
  55.  
  56. #include "HTML.h"
  57. #include "mosaic.h"
  58. #include <time.h>
  59. #include <Xm/List.h>
  60. #include <pwd.h>
  61. #include <sys/types.h>
  62.  
  63. #include "bitmaps/hotlist.xbm"
  64.  
  65. /* This file provides support for hotlists of interesting
  66.    documents within the browser.
  67.    
  68.    Initially there will be a single hotlist, 'Default'.
  69.  
  70.    Initially the hotlist file format will look like this:
  71.    
  72.    ncsa-mosaic-hotlist-format-1            [identifying string]
  73.    Default                                 [title]
  74.    url Fri Sep 13 00:00:00 1986            [first word is url;
  75.                                             subsequent words are
  76.                                             last-accessed date (GMT)]
  77.    document title cached here              [cached title for above]
  78.    [2-line sequence for single document repeated as necessary]
  79.    ...
  80.    
  81.    Turns out this format is bad for two reasons:
  82.    (1) Document titles can have embedded carriage returns (usually
  83.        on purpose).
  84.    (2) URL's can have embedded carriage returns (usually on accident).
  85.  
  86.    Also, we should just be using an HTML-derived format for hotlists.
  87. */
  88.  
  89. #define NCSA_HOTLIST_FORMAT_COOKIE_ONE \
  90.   "ncsa-xmosaic-hotlist-format-1"
  91.   
  92. static mo_hotlist *default_hotlist = NULL;
  93.  
  94. /*
  95.  * Given a hotlist and a hotnode, append the node
  96.  * to the hotlist.
  97.  * Change fields nodelist and nodelist_last in the hotlist,
  98.  * and fields next and previous in the hotnode.
  99.  * Also fill in field position in the hotnode.
  100.  * Return nothing.
  101.  */
  102. static void mo_append_hotnode_to_hotlist (mo_hotlist *list, 
  103.                                           mo_hotnode *node)
  104. {
  105.   if (list->nodelist == 0)
  106.     {
  107.       /* Nothing yet. */
  108.       list->nodelist = node;
  109.       list->nodelist_last = node;
  110.       node->next = 0;
  111.       node->previous = 0;
  112.       node->position = 1;
  113.     }
  114.   else
  115.     {
  116.       /* The new node becomes nodelist_last. */
  117.       /* But first, set up node. */
  118.       node->previous = list->nodelist_last;
  119.       node->next = 0;
  120.       node->position = node->previous->position + 1;
  121.       
  122.       /* Now point forward from previous nodelist_last. */
  123.       list->nodelist_last->next = node;
  124.       
  125.       /* Now set up new nodelist_last. */
  126.       list->nodelist_last = node;
  127.     }
  128.   
  129.   return;
  130. }
  131.  
  132. /* --------------- mo_delete_position_from_default_hotlist ---------------- */
  133.  
  134. /* Given a hotlist and a hotnode, rip the hotnode out of the hotlist.
  135.    No check is made as to whether the hotnode is actually in the hotlist;
  136.    it better be. */
  137. static void mo_remove_hotnode_from_hotlist (mo_hotlist *list,
  138.                                             mo_hotnode *hotnode)
  139. {
  140.   if (hotnode->previous == NULL)
  141.     {
  142.       /* Node was the first member of the list. */
  143.       if (hotnode->next != NULL)
  144.         {
  145.           /* Node was the first member of the list and had
  146.              a next node. */
  147.           /* The next node is now the first node in the list. */
  148.           hotnode->next->previous = NULL;
  149.           list->nodelist = hotnode->next;
  150.         }
  151.       else
  152.         {
  153.           /* Node was the first member of the list and
  154.              didn't have a next node. */
  155.           /* The list is now empty. */
  156.           list->nodelist = NULL;
  157.           list->nodelist_last = NULL;
  158.         }
  159.     }
  160.   else
  161.     {
  162.       /* Node had a previous. */
  163.       if (hotnode->next != NULL)
  164.         {
  165.           /* Node had a previous and a next. */
  166.           hotnode->previous->next = hotnode->next;
  167.           hotnode->next->previous = hotnode->previous;
  168.         }
  169.       else
  170.         {
  171.           /* Node had a previous but no next. */
  172.           hotnode->previous->next = NULL;
  173.           list->nodelist_last = hotnode->previous;
  174.         }
  175.     }
  176.   
  177.   return;
  178. }
  179.  
  180. /* Go through a hotlist (sequence of hotnodes) and assign position
  181.    numbers for all of 'em. */
  182. static void mo_recalculate_hotlist_positions (mo_hotlist *list)
  183. {
  184.   mo_hotnode *hotnode;
  185.   int count = 1;
  186.   
  187.   for (hotnode = list->nodelist; hotnode != NULL;
  188.        hotnode = hotnode->next)
  189.     hotnode->position = count++;
  190.   
  191.   return;
  192. }
  193.  
  194. /*
  195.  * Delete an element of the default hotlist.
  196.  * The element is referenced by its position.
  197.  * Algorithm for removal:
  198.  *   Find hotnode with the position.
  199.  *   Remove the hotnode from the hotlist data structure.
  200.  *   Recalculate positions of the hotlist.
  201.  *   Remove the element in the position in the list widgets.
  202.  * Return status.
  203.  */
  204. mo_status mo_delete_position_from_default_hotlist (int position)
  205. {
  206.   mo_hotlist *list = default_hotlist;
  207.   mo_hotnode *hotnode;
  208.   mo_window *win = NULL;
  209.   
  210.   for (hotnode = list->nodelist; hotnode != NULL;
  211.        hotnode = hotnode->next)
  212.     {
  213.       if (hotnode->position == position)
  214.         goto foundit;
  215.     }
  216.   
  217.   return mo_fail;
  218.   
  219.   /* OK, now we have hotnode loaded. */
  220.  foundit:
  221.   /* Pull the hotnode out of the hotlist. */
  222.   mo_remove_hotnode_from_hotlist (list, hotnode);
  223.   free (hotnode);
  224.   /* Recalculate positions in this hotlist. */
  225.   mo_recalculate_hotlist_positions (list);
  226.   
  227.   /* Do the GUI stuff. */
  228.   while (win = mo_next_window (win))
  229.     {
  230.       if (win->hotlist_list)
  231.         XmListDeletePos (win->hotlist_list, position);
  232.     }
  233.   
  234.   return mo_succeed;
  235. }
  236.  
  237. /* ------------------- mo_edit_title_in_default_hotlist ------------------- */
  238.  
  239. static XmxCallback (edithot_cb)
  240. {
  241.   mo_window *win = mo_fetch_window_by_id 
  242.     (XmxExtractUniqid ((int)client_data));
  243.   char *title;
  244.  
  245.   switch (XmxExtractToken ((int)client_data))
  246.     {
  247.     case 0:
  248.       XtUnmanageChild (win->edithot_win);
  249.       title = XmxTextGetString (win->edithot_text);
  250.       {
  251.         /* OK, now position is still cached in win->edithot_pos. */
  252.         mo_hotlist *list = default_hotlist;
  253.         mo_hotnode *hotnode;
  254.         
  255.         for (hotnode = list->nodelist; hotnode != NULL;
  256.              hotnode = hotnode->next)
  257.           {
  258.             if (hotnode->position == win->edithot_pos)
  259.               goto foundit;
  260.           }
  261.       foundit:
  262.         if (hotnode == NULL)
  263.           goto punt;
  264.         if (hotnode->position != win->edithot_pos)
  265.           goto punt;
  266.  
  267.         /* OK, now we have the hotnode. */
  268.         hotnode->title = title;
  269.         
  270.         /* Save the hotlist before we screw something up. */
  271.         mo_write_default_hotlist ();
  272.  
  273.         /* Change the extant hotlists. */
  274.         {
  275.           mo_window *win = NULL;
  276.  
  277.           while (win = mo_next_window (win))
  278.             if (win->hotlist_list)
  279.               {
  280.                 XmString xmstr =
  281.                   XmxMakeXmstrFromString (Rdata.display_urls_not_titles ? 
  282.                                           hotnode->url : hotnode->title);
  283.                 XmListDeletePos
  284.                   (win->hotlist_list,
  285.                    hotnode->position);
  286.                 XmListAddItemUnselected 
  287.                   (win->hotlist_list, 
  288.                    xmstr, 
  289.                    hotnode->position);
  290.                 XmStringFree (xmstr);
  291.               }
  292.         }
  293.         
  294.         /* That's it! */
  295.       }
  296.   
  297.     punt:
  298.       break;
  299.     case 1:
  300.       XtUnmanageChild (win->edithot_win);
  301.       /* Do nothing. */
  302.       break;
  303.     case 2:
  304.       mo_open_another_window
  305.         (win, 
  306.          mo_assemble_help_url ("help-on-hotlist-view.html"), 
  307.          NULL, NULL);
  308.       break;
  309.     }
  310.  
  311.   return;
  312. }
  313.  
  314. /* If it don't exist, make it... */
  315. static mo_status mo_create_edithot_win (mo_window *win)
  316. {
  317.   Widget dialog_frame;
  318.   Widget dialog_sep, buttons_form;
  319.   Widget eht_form, title_label;
  320.   
  321.   XmxSetUniqid (win->id);
  322.   win->edithot_win = XmxMakeFormDialog 
  323.     (win->hotlist_win, "NCSA Mosaic: Edit Hotlist Entry Title");
  324.   dialog_frame = XmxMakeFrame (win->edithot_win, XmxShadowOut);
  325.   
  326.   /* Constraints for win->edithot_win. */
  327.   XmxSetConstraints 
  328.     (dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
  329.      XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
  330.   
  331.   /* Main form. */
  332.   eht_form = XmxMakeForm (dialog_frame);
  333.   
  334.   title_label = XmxMakeLabel (eht_form, "Entry Title: ");
  335.   XmxSetArg (XmNwidth, 335);
  336.   win->edithot_text = XmxMakeTextField (eht_form);
  337.   XmxAddCallbackToText (win->edithot_text, edithot_cb, 0);
  338.   
  339.   dialog_sep = XmxMakeHorizontalSeparator (eht_form);
  340.   
  341.   buttons_form = XmxMakeFormAndThreeButtons
  342.     (eht_form, edithot_cb, "Commit", "Dismiss", "Help...", 0, 1, 2);
  343.   
  344.   /* Constraints for eht_form. */
  345.   XmxSetOffsets (title_label, 14, 0, 10, 0);
  346.   XmxSetConstraints
  347.     (title_label, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_NONE,
  348.      NULL, NULL, NULL, NULL);
  349.   XmxSetOffsets (win->edithot_text, 10, 0, 5, 10);
  350.   XmxSetConstraints
  351.     (win->edithot_text, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_WIDGET,
  352.      XmATTACH_FORM, NULL, NULL, title_label, NULL);
  353.   
  354.   XmxSetArg (XmNtopOffset, 10);
  355.   XmxSetConstraints 
  356.     (dialog_sep, XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, 
  357.      XmATTACH_FORM,
  358.      win->edithot_text, buttons_form, NULL, NULL);
  359.   XmxSetConstraints 
  360.     (buttons_form, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
  361.      XmATTACH_FORM,
  362.      NULL, NULL, NULL, NULL);
  363.   
  364.   return mo_succeed;
  365. }
  366.  
  367.   
  368. static mo_status mo_do_edit_hotnode_title_win (mo_window *win, 
  369.                                                char *title, int position)
  370. {
  371.   /* This shouldn't happen. */
  372.   if (!win->hotlist_win)
  373.     return mo_fail;
  374.   
  375.   if (!win->edithot_win)
  376.     mo_create_edithot_win (win);
  377.  
  378.   /* Cache the position. */
  379.   win->edithot_pos = position;
  380.  
  381.   /* Insert this title as a starting point. */
  382.   XmxTextSetString (win->edithot_text, title);
  383.  
  384.   /* Manage the little sucker. */
  385.   XtManageChild (win->edithot_win);
  386.   
  387.   return mo_succeed;
  388. }
  389.  
  390.  
  391. /*
  392.  * Edit the title of an element of the default hotlist.
  393.  * The element is referenced by its position.
  394.  * Algorithm for edit:
  395.  *   Find hotnode with the position.
  396.  *   Change the title.
  397.  *   Cause redisplay.
  398.  * Return status.
  399.  */
  400. mo_status mo_edit_title_in_default_hotlist (mo_window *win, int position)
  401. {
  402.   mo_hotlist *list = default_hotlist;
  403.   mo_hotnode *hotnode;
  404.   
  405.   for (hotnode = list->nodelist; hotnode != NULL;
  406.        hotnode = hotnode->next)
  407.     {
  408.       if (hotnode->position == position)
  409.         goto foundit;
  410.     }
  411.   
  412.   return mo_fail;
  413.   
  414.   /* OK, now we have hotnode loaded. */
  415.  foundit:
  416.   /* hotnode->title is the current title.
  417.      hotnode->position is the current position. */
  418.   mo_do_edit_hotnode_title_win (win, hotnode->title, hotnode->position);
  419.   
  420.   return mo_succeed;
  421. }
  422.  
  423. /* --------------------------- mo_read_hotlist ---------------------------- */
  424.  
  425. /*
  426.  * Read a hotlist from a file.
  427.  * Return pointer to a mo_hotlist structure, fully loaded
  428.  * and ready to go.
  429.  * Return NULL if file does not exist or is not readable.
  430.  */
  431. mo_hotlist *mo_read_hotlist (char *filename)
  432. {
  433.   mo_hotlist *list = NULL;
  434.   FILE *fp;
  435.   char line[MO_LINE_LENGTH];
  436.   char *status;
  437.   
  438.   fp = fopen (filename, "r");
  439.   if (!fp)
  440.     goto screwed_no_file;
  441.   
  442.   status = fgets (line, MO_LINE_LENGTH, fp);
  443.   if (!status || !(*line))
  444.     goto screwed_open_file;
  445.   
  446.   /* See if it's our format. */
  447.   if (strncmp (line, NCSA_HOTLIST_FORMAT_COOKIE_ONE,
  448.                strlen (NCSA_HOTLIST_FORMAT_COOKIE_ONE)))
  449.     goto screwed_open_file;
  450.   
  451.   /* Hey, whaddaya know, it is. */
  452.   list = (mo_hotlist *)malloc (sizeof (mo_hotlist));
  453.   list->nodelist = list->nodelist_last = 0;
  454.   list->filename = filename;
  455.   list->modified = 1;
  456.   list->next = 0;
  457.   
  458.   /* Go fetch the name on the next line. */
  459.   status = fgets (line, MO_LINE_LENGTH, fp);
  460.   if (!status || (!*line))
  461.     {
  462.       free (list);
  463.       list = NULL;
  464.       goto screwed_open_file;
  465.     }
  466.   list->name = strtok (line, "\n");
  467.   if (!list->name)
  468.     {
  469.       free (list);
  470.       list = NULL;
  471.       goto screwed_open_file;
  472.     }      
  473.   list->name = strdup (list->name);
  474.  
  475.   /* Start grabbing documents. */
  476.   while (1)
  477.     {
  478.       mo_hotnode *node;
  479.       
  480.       status = fgets (line, MO_LINE_LENGTH, fp);
  481.       if (!status || !(*line))
  482.         goto done;
  483.       
  484.       /* We've got a new node. */
  485.       node = (mo_hotnode *)malloc (sizeof (mo_hotnode));
  486.       
  487.       node->url = strtok (line, " ");
  488.       if (!node->url)
  489.         goto screwed_open_file;
  490.       node->url = strdup (node->url);
  491.       mo_convert_newlines_to_spaces (node->url);
  492.  
  493.       node->lastdate = strtok (NULL, "\n");
  494.       if (!node->lastdate)
  495.         goto screwed_open_file;
  496.       node->lastdate = strdup (node->lastdate);
  497.       
  498.       status = fgets (line, MO_LINE_LENGTH, fp);
  499.       if (!status || !(*line))
  500.         {
  501.           /* Oops, something went wrong. */
  502.           free (node->url);
  503.           free (node->lastdate);
  504.           free (node);
  505.           goto done;
  506.         }
  507.       
  508.       node->title = strtok (line, "\n");
  509.       if (!node->title)
  510.         goto screwed_open_file;
  511.       node->title = strdup (node->title);
  512.       mo_convert_newlines_to_spaces (node->url);
  513.       
  514.       mo_append_hotnode_to_hotlist (list, node);
  515.     }
  516.   
  517.  done:
  518.   fclose (fp);
  519.   return list;
  520.  
  521.  screwed_open_file:
  522.   fclose (fp);
  523.   return list;
  524.  
  525.  screwed_no_file:
  526.   return NULL;
  527. }
  528.  
  529. /*
  530.  * Create a new mo_hotlist.
  531.  * Pass in the new filename and the new title.
  532.  */
  533. mo_hotlist *mo_new_hotlist (char *filename, char *title)
  534. {
  535.   mo_hotlist *list;
  536.   
  537.   list = (mo_hotlist *)malloc (sizeof (mo_hotlist));
  538.   list->nodelist = list->nodelist_last = 0;
  539.   list->filename = filename;
  540.   list->modified = 1;
  541.   list->next = 0;
  542.   list->name = strdup (title);
  543.   return list;
  544. }
  545.  
  546. /*
  547.  * Write a hotlist out to a file.
  548.  * Return mo_succeed if everything goes OK;
  549.  * mo_fail else.
  550.  */
  551. mo_status mo_write_hotlist (mo_hotlist *list)
  552. {
  553.   FILE *fp;
  554.   mo_hotnode *node;
  555.   
  556.   fp = fopen (list->filename, "w");
  557.   if (!fp)
  558.     return mo_fail;
  559.   
  560.   fprintf (fp, "%s\n%s\n", NCSA_HOTLIST_FORMAT_COOKIE_ONE, list->name);
  561.   
  562.   for (node = list->nodelist; node != NULL; node = node->next)
  563.     fprintf (fp, "%s %s\n%s\n", node->url, node->lastdate, node->title);
  564.  
  565.   fclose (fp);
  566.   
  567.   /* We can consider it unmodified since latest write now. */
  568.   list->modified = 0;
  569.   return mo_succeed;
  570. }
  571.  
  572. /*
  573.  * Write a hotlist out to stdout.
  574.  * Return mo_succeed if everything goes OK;
  575.  * mo_fail else.
  576.  */
  577. mo_status mo_dump_hotlist (mo_hotlist *list)
  578. {
  579.   FILE *fp;
  580.   mo_hotnode *node;
  581.   
  582.   fp = stdout;
  583.   
  584.   fprintf (fp, NCSA_HOTLIST_FORMAT_COOKIE_ONE);
  585.   fprintf (fp, "\n");
  586.   
  587.   for (node = list->nodelist; node != NULL; node = node->next)
  588.     {
  589.       fprintf (fp, "%s %s\n%s\n", node->url, node->lastdate, node->title);
  590.     }
  591.   
  592.   return mo_succeed;
  593. }
  594.  
  595. /* ------------------------------------------------------------------------ */
  596. /* ----------------------------- HOTLIST GUI ------------------------------ */
  597. /* ------------------------------------------------------------------------ */
  598.  
  599. /* Initial GUI support for hotlist will work like this:
  600.  
  601.    There will be a single hotlist, called 'Default'.
  602.    It will be persistent across all windows.
  603.  
  604.    Upon program startup an attempt will be made to load it out
  605.    of its file; if this attempt isn't successful, it just plain
  606.    doesn't exist yet.  Bummer.
  607.    
  608.    Upon program exit it will be stored to its file.
  609. */
  610.  
  611. /*
  612.  * Called on initialization.  
  613.  * Tries to load the default hotlist.
  614.  */
  615. mo_status mo_setup_default_hotlist (void)
  616. {
  617.   char *home = getenv ("HOME");
  618.   char *default_filename = Rdata.default_hotlist_file;
  619.   char *filename;
  620.   
  621.   /* This shouldn't happen. */
  622.   if (!home)
  623.     home = "/tmp";
  624.   
  625.   filename = (char *)malloc 
  626.     ((strlen (home) + strlen (default_filename) + 8) * sizeof (char));
  627.   sprintf (filename, "%s/%s\0", home, default_filename);
  628.   
  629.   /* Try to load the default hotlist. */
  630.   default_hotlist = mo_read_hotlist (filename);
  631.   /* Doesn't exist?  Bummer.  Make a new one. */
  632.   if (!default_hotlist)
  633.     default_hotlist = mo_new_hotlist (filename, "Default");
  634.   
  635.   return mo_succeed;
  636. }
  637.  
  638. /*
  639.  * Called on program exit.
  640.  * Tries to write the default hotlist.
  641.  */
  642. mo_status mo_write_default_hotlist (void)
  643. {
  644.   mo_write_hotlist (default_hotlist);
  645.   
  646.   return mo_succeed;
  647. }
  648.  
  649. /* ------------------------------------------------------------------------ */
  650. /* ------------------------- gui support routines ------------------------- */
  651. /* ------------------------------------------------------------------------ */
  652.  
  653. /* We've just init'd a new hotlist list widget; look at the default
  654.    hotlist and load 'er up. */
  655. static void mo_load_hotlist_list (mo_window *win, Widget list)
  656. {
  657.   mo_hotnode *node;
  658.   
  659.   for (node = default_hotlist->nodelist; node != NULL; node = node->next)
  660.     {
  661.       XmString xmstr = 
  662.         XmxMakeXmstrFromString (Rdata.display_urls_not_titles ? 
  663.                                 node->url : node->title);
  664.       XmListAddItemUnselected 
  665.         (list, xmstr, 0);
  666.       XmStringFree (xmstr);
  667.     }
  668.  
  669.   return;
  670. }
  671.  
  672. static void mo_visit_hotlist_position (mo_window *win, int position)
  673. {
  674.   mo_hotnode *hotnode;
  675.  
  676.   for (hotnode = default_hotlist->nodelist; hotnode != NULL;
  677.        hotnode = hotnode->next)
  678.     {
  679.       if (hotnode->position == position)
  680.         mo_access_document (win, hotnode->url);
  681.     }
  682.   
  683.   return;
  684. }
  685.  
  686. /* ----------------------------- mail hotlist ----------------------------- */
  687.  
  688. static XmxCallback (mailhot_win_cb)
  689. {
  690.   mo_window *win = mo_fetch_window_by_id 
  691.     (XmxExtractUniqid ((int)client_data));
  692.   char *to, *subj, *fnam, *cmd;
  693.   FILE *fp;
  694.  
  695.   switch (XmxExtractToken ((int)client_data))
  696.     {
  697.     case 0:
  698.       XtUnmanageChild (win->mailhot_win);
  699.  
  700.       mo_busy ();
  701.  
  702.       to = XmxTextGetString (win->mailhot_to_text);
  703.       if (!to)
  704.         return;
  705.       if (to[0] == '\0')
  706.         return;
  707.  
  708.       subj = XmxTextGetString (win->mailhot_subj_text);
  709.  
  710.       fnam = mo_tmpnam();
  711.  
  712.       fp = fopen (fnam, "w");
  713.       if (!fp)
  714.         goto oops;
  715.  
  716.       {
  717.         mo_hotnode *node;
  718.         struct passwd *pw = getpwuid (getuid ());
  719.         char *author;
  720.   
  721.         if (Rdata.default_author_name)
  722.           author = Rdata.default_author_name;
  723.         else
  724.           author = pw->pw_gecos;
  725.         
  726.         fprintf (fp, "<H1>Hotlist From %s</H1>\n", author);
  727.         fprintf (fp, "<DL>\n");
  728.         for (node = default_hotlist->nodelist; node != NULL; node = node->next)
  729.           {
  730.             fprintf (fp, "<DT>%s\n<DD><A HREF=\"%s\">%s</A>\n", 
  731.                      node->title, node->url, node->url);
  732.           }
  733.         fprintf (fp, "</DL>\n");
  734.       }
  735.         
  736.       fclose (fp);
  737.  
  738.       cmd = (char *)malloc 
  739.         ((strlen (Rdata.mail_command) + strlen (subj) + strlen (to)
  740.                              + strlen (fnam) + 24));
  741.       sprintf (cmd, "%s -s \"%s\" %s < %s", 
  742.                Rdata.mail_command, subj, to, fnam);
  743.       if ((system (cmd)) != 0)
  744.         {
  745.           XmxMakeErrorDialog 
  746.             (win->base,
  747.              "Unable to mail hotlist;\ncheck the value of the X resource\nmailCommand.", 
  748.              "Mail Hotlist Error");
  749.           XtManageChild (Xmx_w);
  750.         }
  751.       free (cmd);
  752.  
  753.     oops:
  754.       free (to);
  755.       free (subj);
  756.  
  757.       cmd = (char *)malloc ((strlen (fnam) + 32) * sizeof (char));
  758.       sprintf (cmd, "/bin/rm -f %s &", fnam);
  759.       system (cmd);
  760.  
  761.       free (fnam);
  762.       free (cmd);
  763.  
  764.       mo_not_busy ();
  765.             
  766.       break;
  767.     case 1:
  768.       XtUnmanageChild (win->mailhot_win);
  769.       /* Do nothing. */
  770.       break;
  771.     case 2:
  772.       mo_open_another_window
  773.         (win, 
  774.          mo_assemble_help_url ("help-on-hotlist-view.html"), 
  775.          NULL, NULL);
  776.       break;
  777.     }
  778.  
  779.   return;
  780. }
  781.  
  782. static mo_status mo_post_mailhot_win (mo_window *win)
  783. {
  784.   /* This shouldn't happen. */
  785.   if (!win->hotlist_win)
  786.     return mo_fail;
  787.  
  788.   if (!win->mailhot_win)
  789.     {
  790.       Widget dialog_frame;
  791.       Widget dialog_sep, buttons_form;
  792.       Widget mailhot_form, to_label, subj_label;
  793.       
  794.       /* Create it for the first time. */
  795.       XmxSetUniqid (win->id);
  796.       win->mailhot_win = XmxMakeFormDialog 
  797.         (win->hotlist_win, "NCSA Mosaic: Mail Hotlist");
  798.       dialog_frame = XmxMakeFrame (win->mailhot_win, XmxShadowOut);
  799.  
  800.       /* Constraints for base. */
  801.       XmxSetConstraints 
  802.         (dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
  803.          XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
  804.       
  805.       /* Main form. */
  806.       mailhot_form = XmxMakeForm (dialog_frame);
  807.       
  808.       to_label = XmxMakeLabel (mailhot_form, "Mail To: ");
  809.       XmxSetArg (XmNwidth, 335);
  810.       win->mailhot_to_text = XmxMakeTextField (mailhot_form);
  811.       
  812.       subj_label = XmxMakeLabel (mailhot_form, "Subject: ");
  813.       win->mailhot_subj_text = XmxMakeTextField (mailhot_form);
  814.  
  815.       dialog_sep = XmxMakeHorizontalSeparator (mailhot_form);
  816.       
  817.       buttons_form = XmxMakeFormAndThreeButtons
  818.         (mailhot_form, mailhot_win_cb, "Mail", "Dismiss", "Help...", 0, 1, 2);
  819.  
  820.       /* Constraints for mailhot_form. */
  821.       XmxSetOffsets (to_label, 14, 0, 10, 0);
  822.       XmxSetConstraints
  823.         (to_label, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_NONE,
  824.          NULL, NULL, NULL, NULL);
  825.       XmxSetOffsets (win->mailhot_to_text, 10, 0, 5, 10);
  826.       XmxSetConstraints
  827.         (win->mailhot_to_text, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_WIDGET,
  828.          XmATTACH_FORM, NULL, NULL, to_label, NULL);
  829.  
  830.       XmxSetOffsets (subj_label, 14, 0, 10, 0);
  831.       XmxSetConstraints
  832.         (subj_label, XmATTACH_WIDGET, XmATTACH_NONE, XmATTACH_FORM, 
  833.          XmATTACH_NONE,
  834.          win->mailhot_to_text, NULL, NULL, NULL);
  835.       XmxSetOffsets (win->mailhot_subj_text, 10, 0, 5, 10);
  836.       XmxSetConstraints
  837.         (win->mailhot_subj_text, XmATTACH_WIDGET, 
  838.          XmATTACH_NONE, XmATTACH_WIDGET,
  839.          XmATTACH_FORM, win->mailhot_to_text, NULL, subj_label, NULL);
  840.  
  841.       XmxSetArg (XmNtopOffset, 10);
  842.       XmxSetConstraints 
  843.         (dialog_sep, XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, 
  844.          XmATTACH_FORM,
  845.          win->mailhot_subj_text, buttons_form, NULL, NULL);
  846.       XmxSetConstraints 
  847.         (buttons_form, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
  848.          XmATTACH_FORM,
  849.          NULL, NULL, NULL, NULL);
  850.     }
  851.   
  852.   XtManageChild (win->mailhot_win);
  853.   
  854.   return mo_succeed;
  855. }
  856.  
  857. /* ---------------------------- hotlist_win_cb ---------------------------- */
  858.  
  859. static XmxCallback (hotlist_win_cb)
  860. {
  861.   mo_window *win = mo_fetch_window_by_id (XmxExtractUniqid ((int)client_data));
  862.  
  863.   switch (XmxExtractToken ((int)client_data))
  864.     {
  865.     case 0:
  866.       XtUnmanageChild (win->hotlist_win);
  867.       /* Dismissed -- do nothing. */
  868.       break;
  869.     case 1:
  870.       mo_post_mailhot_win (win);
  871.       break;
  872.     case 2:
  873.       mo_open_another_window
  874.         (win, 
  875.          mo_assemble_help_url ("help-on-hotlist-view.html"),
  876.          NULL, NULL);
  877.       break;
  878.     case 3:
  879.       /* Add current. */
  880.       mo_add_node_to_default_hotlist (win->current_node);
  881.       mo_write_default_hotlist ();
  882.       break;
  883.     case 4:
  884.       /* Goto selected. */
  885.       {
  886.         Boolean rv;
  887.         int *pos_list;
  888.         int pos_cnt;
  889.         rv = XmListGetSelectedPos (win->hotlist_list, &pos_list, &pos_cnt);
  890.         if (rv && pos_cnt)
  891.           {
  892.             mo_visit_hotlist_position (win, pos_list[0]);
  893.           }
  894.         else
  895.           {
  896.             XmxMakeErrorDialog
  897.               (win->hotlist_win, "No entry in the hotlist is currently selected.\n\nTo go to an entry in the hotlist,\nselect it with a single mouse click\nand press the Go To button again.", "Error: Nothing Selected");
  898.             XtManageChild (Xmx_w);
  899.           }
  900.       }
  901.       break;
  902.     case 5:
  903.       /* Remove selected. */
  904.       {
  905.         Boolean rv;
  906.         int *pos_list;
  907.         int pos_cnt;
  908.         rv = XmListGetSelectedPos (win->hotlist_list, &pos_list, &pos_cnt);
  909.         if (rv && pos_cnt)
  910.           {
  911.             mo_delete_position_from_default_hotlist (pos_list[0]);
  912.             mo_write_default_hotlist ();
  913.           }
  914.         else
  915.           {
  916.             XmxMakeErrorDialog
  917.               (win->hotlist_win, "No entry in the hotlist is currently selected.\n\nTo remove an entry in the hotlist,\nselect it with a single mouse click\nand press the Remove button again.", "Error: Nothing Selected");
  918.             XtManageChild (Xmx_w);
  919.           }
  920.       }
  921.       break;
  922.     case 6:
  923.       /* Edit title of selected. */
  924.       {
  925.         Boolean rv;
  926.         int *pos_list;
  927.         int pos_cnt;
  928.         rv = XmListGetSelectedPos (win->hotlist_list, &pos_list, &pos_cnt);
  929.         if (rv && pos_cnt)
  930.           {
  931.             mo_edit_title_in_default_hotlist (win, pos_list[0]);
  932.             /* Writing the default hotlist should take place in the callback. */
  933.             /* mo_write_default_hotlist (); */
  934.           }
  935.         else
  936.           {
  937.             XmxMakeErrorDialog
  938.               (win->hotlist_win, "No entry in the hotlist is currently selected.\n\nTo edit the title of an entry in the hotlist,\nselect it with a single mouse click\nand press the Edit Title button again.", "Error: Nothing Selected");
  939.             XtManageChild (Xmx_w);
  940.           }
  941.       }
  942.       break;
  943.     }
  944.  
  945.   return;
  946. }
  947.  
  948. static XmxCallback (hotlist_list_cb)
  949. {
  950.   mo_window *win = mo_fetch_window_by_id (XmxExtractUniqid ((int)client_data));
  951.   XmListCallbackStruct *cs = (XmListCallbackStruct *)call_data;
  952.   
  953.   mo_visit_hotlist_position (win, cs->item_position);
  954.   
  955.   /* Don't unmanage the list. */
  956.   
  957.   return;
  958. }
  959.  
  960. /* ------------------------- mo_post_hotlist_win -------------------------- */
  961.  
  962. /*
  963.  * Pop up a hotlist window for an mo_window.
  964.  */
  965. mo_status mo_post_hotlist_win (mo_window *win)
  966. {
  967.   if (!win->hotlist_win)
  968.     {
  969.       Widget dialog_frame;
  970.       Widget dialog_sep, buttons_form, buttons1_form;
  971.       Widget hotlist_form, buttons1_frame;
  972.       Widget label, logo;
  973.       XtTranslations listTable;
  974.       static char listTranslations[] =
  975.     "~Shift ~Ctrl ~Meta ~Alt <Btn2Down>: ListBeginSelect() \n\
  976.      ~Shift ~Ctrl ~Meta ~Alt <Btn2Up>:   ListEndSelect() ListKbdActivate()";
  977.  
  978.       listTable = XtParseTranslationTable(listTranslations);
  979.  
  980.       /* Create it for the first time. */
  981.       XmxSetUniqid (win->id);
  982.       XmxSetArg (XmNwidth, 475);
  983.       XmxSetArg (XmNheight, 352);
  984.       win->hotlist_win = XmxMakeFormDialog 
  985.         (win->base, "NCSA Mosaic: Hotlist View");
  986.       dialog_frame = XmxMakeFrame (win->hotlist_win, XmxShadowOut);
  987.       
  988.       /* Constraints for base. */
  989.       XmxSetConstraints 
  990.         (dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
  991.          XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
  992.       
  993.       /* Main form. */
  994.       hotlist_form = XmxMakeForm (dialog_frame);
  995.  
  996.       label = XmxMakeLabel (hotlist_form, "Default Hotlist:");
  997.  
  998.       buttons1_form = XmxMakeFormAndFourButtons
  999.         (hotlist_form, hotlist_win_cb, "Add Current", "Go To",
  1000.          "Remove", "Edit Title", 3, 4, 5, 6);
  1001.  
  1002.       logo = XmxMakeNamedLabel (hotlist_form, NULL, "logo");
  1003.       XmxApplyBitmapToLabelWidget
  1004.         (logo, hotlist_bits, hotlist_width, hotlist_height);
  1005.       
  1006.       /* Hotlist list itself. */
  1007.       XmxSetArg (XmNresizable, False);
  1008.       XmxSetArg (XmNscrollBarDisplayPolicy, XmSTATIC);
  1009.       XmxSetArg (XmNlistSizePolicy, XmCONSTANT);
  1010.       win->hotlist_list = 
  1011.         XmxMakeScrolledList (hotlist_form, hotlist_list_cb, 0);
  1012.       XtAugmentTranslations (win->hotlist_list, listTable);
  1013.  
  1014.       dialog_sep = XmxMakeHorizontalSeparator (hotlist_form);
  1015.       
  1016.       buttons_form = XmxMakeFormAndThreeButtons
  1017.         (hotlist_form, hotlist_win_cb, "Dismiss", "Mail To...", "Help...", 
  1018.          0, 1, 2);
  1019.       
  1020.       /* Constraints for hotlist_form. */
  1021.       /* Label: top to nothing, bottom to buttons1_form,
  1022.          left to form, right to nothing. */
  1023.       XmxSetOffsets (label, 0, 5, 7, 10);
  1024.       XmxSetConstraints
  1025.         (label, XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM,
  1026.          XmATTACH_NONE, NULL, buttons1_form, NULL, NULL);
  1027.       /* buttons1_form: top to nothing, bottom to hotlist_list,
  1028.          left to form, right to logo. */
  1029.       XmxSetOffsets (buttons1_form, 0, 2, 2, 0);
  1030.       XmxSetConstraints
  1031.         (buttons1_form, XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM,
  1032.          XmATTACH_WIDGET, 
  1033.          NULL, XtParent (win->hotlist_list), NULL, logo);
  1034.       /* Logo: top to form, bottom to nothing,
  1035.          left to nothing, right to form. */
  1036.       XmxSetOffsets (logo, 2, 0, 0, 4);
  1037.       XmxSetConstraints
  1038.         (logo, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_NONE, XmATTACH_FORM,
  1039.          NULL, NULL, NULL, NULL);
  1040.       /* list: top to logo, bottom to dialog_sep,
  1041.          etc... */
  1042.       XmxSetOffsets (XtParent (win->hotlist_list), 10, 10, 10, 10);
  1043.       XmxSetConstraints
  1044.         (XtParent (win->hotlist_list), 
  1045.          XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, XmATTACH_FORM, 
  1046.          logo, dialog_sep, NULL, NULL);
  1047.       XmxSetConstraints 
  1048.         (dialog_sep, 
  1049.          XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM, XmATTACH_FORM,
  1050.          NULL, buttons_form, NULL, NULL);
  1051.       XmxSetConstraints 
  1052.         (buttons_form, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
  1053.          XmATTACH_FORM,
  1054.          NULL, NULL, NULL, NULL);
  1055.       
  1056.       /* Go get the hotlist up to this point set up... */
  1057.       mo_load_hotlist_list (win, win->hotlist_list);
  1058.     }
  1059.   
  1060.   XtManageChild (win->hotlist_win);
  1061.   
  1062.   return mo_succeed;
  1063. }
  1064.  
  1065. /* -------------------- mo_add_node_to_default_hotlist -------------------- */
  1066.  
  1067. mo_status mo_add_node_to_hotlist (mo_hotlist *list, mo_node *node)
  1068. {
  1069.   mo_hotnode *hotnode = (mo_hotnode *)malloc (sizeof (mo_hotnode));
  1070.   mo_window *win = NULL;
  1071.   time_t foo = time (NULL);
  1072.   char *ts = ctime (&foo);
  1073.   
  1074.   ts[strlen(ts)-1] = '\0';
  1075.  
  1076.   if (node->title)
  1077.     hotnode->title = strdup (node->title);
  1078.   else
  1079.     hotnode->title = strdup ("Unnamed");
  1080.   mo_convert_newlines_to_spaces (hotnode->title);
  1081.  
  1082.   hotnode->url = strdup (node->url);
  1083.   mo_convert_newlines_to_spaces (hotnode->url);
  1084.  
  1085.   hotnode->lastdate = strdup (ts);
  1086.   
  1087.   mo_append_hotnode_to_hotlist (list, hotnode);
  1088.   
  1089.   /* Now we've got to update all active hotlist_list's. */
  1090.   while (win = mo_next_window (win))
  1091.     {
  1092.       if (win->hotlist_list)
  1093.         {
  1094.           XmString xmstr = 
  1095.             XmxMakeXmstrFromString (Rdata.display_urls_not_titles ? 
  1096.                                     hotnode->url : hotnode->title);
  1097.           XmListAddItemUnselected 
  1098.             (win->hotlist_list, 
  1099.              xmstr, 
  1100.              hotnode->position);
  1101.           XmStringFree (xmstr);
  1102.           XmListSetBottomPos (win->hotlist_list, 0);
  1103.         }
  1104.     }
  1105.   
  1106.   return mo_succeed;
  1107. }
  1108.  
  1109. mo_status mo_add_node_to_default_hotlist (mo_node *node)
  1110. {
  1111.   return mo_add_node_to_hotlist (default_hotlist, node);
  1112. }
  1113.